using System;
using System.Collections.Generic;
using System.Text;
using SP.Parameter;

namespace SP.HwDeferredRS
{
    /// <summary>
    /// A fusion which performs hardware skinning
    /// </summary>
    public class HardwareSkinningFusion : AbstractFusion
    {
        public HardwareSkinningFusion()
        {
            // param sys_BoneMatrix
            AddParameter(
                new Texture2dParamter("sys_BoneMatrix", "sys_BoneMatrixSamp")
                .SetFilters(TextureParamter.Filter.Point, TextureParamter.Filter.Point, TextureParamter.Filter.Point)
                .SetAddressModes(TextureParamter.AddressMode.Clamp, TextureParamter.AddressMode.Clamp)
                .SetSRGBTexture(false)
                .SetEditable(false)
                .SetUserData("vertex")
                );

            // param lightMapMultiplier
            AddParameter(
                new Vec4Parameter("sys_BoneMatrixOffset")
                .SetX(0).SetY(0).SetZ(0)
                .SetUserData("77")
                .SetEditable(false)
                );
        }

        public static string NAME = "HardwareSkinning";
        public override string Name
        {
            get { return NAME; }
        }

        public override string Desc
        {
            get { return "Enable this to support Hardware-Skinning"; }
        }

        public override Stage Stage
        {
            get { return Stage.Vertex0; }
        }

        public override int UVChannelCount
        {
            get { return 0; }
        }

        public override void Code(StringBuilder b, string platform)
        {
            AddLine(b, "vec4 r0 = 0;");
            AddLine(b, "vec4 r1 = 0;");
            AddLine(b, "vec4 r2 = 0;");
            AddLine(b, "float4 v_offset = sys_BoneMatrixOffset.xxxx * 4.0 + vec4(0.5, 1.5, 2.5, 3.5);");
            AddLine(b, "v_offset = v_offset / 128.0;");

            AddLine(b, "for(int i = 0; i < 4; ++i)");
            AddLine(b, "{");

            AddLine(b, "    float weight = input.BoneWeights[i];");
            AddLine(b, "    float index = input.BoneIndices[i];");
            
            AddLine(b, "    float u = (index + 0.5) / 128.0;");

            AddLine(b, "    r0 += tex2Dlod(sys_BoneMatrixSamp, float4(u, v_offset.x, 0.0, 0.0)) * weight;");
            AddLine(b, "    r1 += tex2Dlod(sys_BoneMatrixSamp, float4(u, v_offset.y, 0.0, 0.0)) * weight;");
            AddLine(b, "    r2 += tex2Dlod(sys_BoneMatrixSamp, float4(u, v_offset.z, 0.0, 0.0)) * weight;");

            AddLine(b, "}");

            AddLine(b, "mat4 m = mat4(r0, r1, r2, vec4(0,0,0,1));");

            AddLine(b, "vec4 pos = mul(m, pipeline.Pos_Ms);");
            AddLine(b, "vec3 t = mul((mat3)m, pipeline.T_Ms);");
            //AddLine(b, "vec3 b = mul((mat3)m, pipeline.B_Ms);");
            AddLine(b, "vec3 n = mul((mat3)m, pipeline.N_Ms);");

            AddLine(b, "pipeline.Pos_Ms = pos;");
            AddLine(b, "pipeline.T_Ms = normalize(t);");
            //AddLine(b, "pipeline.B_Ms = normalize(b);");
            AddLine(b, "pipeline.N_Ms = normalize(n);");
        }
    }
}
